package feevale.boteco.bean;

import feevale.boteco.util.Agente;

public class Cliente extends Agente implements Runnable {

	private Integer id;
	private Geladeira geladeira;
	private Cerveja cerveja;

	private Thread thread;

	public Cliente() {
		this.thread = new Thread(this);
	}
	
	public void beber() {

		entraNaFila();
		this.thread.start();
	}

	/**
	 * Consumindo a cerveja. Após o consumo, retorna para a fila de espera de
	 * clientes.
	 * 
	 */
	@Override
	public void run() {
		
		while (true) {
			geladeira.abrirPorta(this);
		}
	}

	/**
	 * Abre a geladeira, buscando uma nova cerveja para consumir.
	 */
	@Override
	public void abreGeladeira(Geladeira geladeira) {
		try {
			
			if (!(geladeira.getCervejas().isEmpty())) {
				Double tempo = getTempoBusca();
				
				saiDaFila(tempo / 1000);
				
				Thread.sleep(tempo.longValue());
				
				pegaACeva();
			}
			
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Ao fechar a geladeira, consome a cerveja (caso haja disponível) e logo após, entra na fila novamente.
	 */
	@Override
	public void fechaGeladeira(Geladeira geladeira) {
		try {
			
			if (temCeva()) {
				Thread.sleep(getTempoConsumo().longValue());
				
				setCerveja(null);
			}
			
			entraNaFila();
			
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Sai da fila de espera e passa a buscar uma nova cerveja na geladeira.
	 * Atualiza dados de acompanhamento do processo.
	 * 
	 * @param tempo
	 */
	private void saiDaFila(Double tempo) {
		
		if (geladeira.getClientesEspera().contains(this)) {

			geladeira.atualizaSituacao(getNome() + " buscando uma ceva!");
			geladeira.atualizaTempoAgente(tempo);

			geladeira.getClientesEspera().remove(this);
			geladeira.atualizaTableClientes();
		}
	}

	/**
	 * Entra na fila de clientes a espera da busca de uma cerveja.
	 */
	private void entraNaFila() {

		if (!geladeira.getClientesEspera().contains(this)) {
			this.geladeira.getClientesEspera().offer(this);
			this.geladeira.atualizaTableClientes();
		}
	}

	/**
	 * Retira da geladeira a próxima cerveja disponível. 
	 * Atualiza dados de acompanhamento do processo.
	 */
	private void pegaACeva() {
		
		Cerveja cerveja = this.geladeira.getCervejas().poll();
		setCerveja(cerveja);
		
		this.geladeira.atualizaTableCervejas();

		geladeira.atualizaSituacao("");
		geladeira.atualizaTempoAgente(null);
	}

	/**
	 * Retorna o tempo (randômico) de busca de uma cerveja na geladeira,
	 * limitando ao intervalo definido na parametrização.
	 * 
	 * @return
	 */
	private static Double getTempoBusca() {
		return getTime(Boteco.DEFAULT_TEMPO_BUSCA_INICIAL, Boteco.DEFAULT_TEMPO_BUSCA_FINAL);
	}

	/**
	 * Retorna o tempo (randômico) de consumo de uma cerveja, limitando ao
	 * intervalo definido na parametrização.
	 * 
	 * @return
	 */
	private static Double getTempoConsumo() {
		return getTime(Boteco.DEFAULT_TEMPO_CONSUMO_INICIAL, Boteco.DEFAULT_TEMPO_CONSUMO_FINAL);
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}
	
	@Override
	public String getNome() {
		return "Cliente " + this.id;
	}
	
	/**
	 * Retorna a geladeira associada ao cliente.
	 * @return
	 */
	public Geladeira getGeladeira() {
		return geladeira;
	}
	
	/**
	 * Atribui ao cliente uma geladeira (de sua "preferência")
	 * @param geladeira
	 */
	public void setGeladeira(Geladeira geladeira) {
		this.geladeira = geladeira;
	}
	
	/**
	 * Atribui ao cliente a cerveja que ele pegou na geladeira.
	 * @param cerveja
	 */
	public void setCerveja(Cerveja cerveja) {
		this.cerveja = cerveja;
	}
	
	/**
	 * Indica se o cliente tem uma ceva para beber.
	 * @return
	 */
	public boolean temCeva() {
		return this.cerveja != null;
	}
	
}
